/**
 * Class to generate polyline
 *
 * @author Dmitry Farafonov
 */

var ANCHOR_TYPE = {
  main: "main",
  middle: "middle",
  first: "first",
  last: "last"
};

function Anchor(uuid, type, x, y) {
  this.uuid = uuid;
  this.x = x
  this.y = y
  this.type = (type == ANCHOR_TYPE.middle) ? ANCHOR_TYPE.middle : ANCHOR_TYPE.main;
};
Anchor.prototype = {
  uuid: null,
  x: 0,
  y: 0,
  type: ANCHOR_TYPE.main,
  isFirst: false,
  isLast: false,
  ndex: 0,
  typeIndex: 0
};

function Polyline(uuid, points, strokeWidth) {
  /* Array on coordinates:
   * points: [{x: 410, y: 110}, 1
   *			{x: 570, y: 110}, 1 2
   *			{x: 620, y: 240},   2 3
   *			{x: 750, y: 270},     3 4
   *			{x: 650, y: 370}];      4
   */
  this.points = points;

  /*
   * path for graph
   * [["M", x1, y1], ["L", x2, y2], ["C", ax, ay, bx, by, x3, y3], ["L", x3, y3]]
   */
  this.path = [];

  this.anchors = [];

  if (strokeWidth) this.strokeWidth = strokeWidth;

  this.closePath = false;

  this.init();
};

Polyline.prototype = {
  id: null,
  points: [],
  path: [],
  anchors: [],
  strokeWidth: 1,
  radius: 15,
  showDetails: false,
  element: null,
  isDefaultConditionAvailable: false,
  closePath: false,

  init: function (points) {
    var linesCount = this.getLinesCount();
    if (linesCount < 1)
      return;

    this.normalizeCoordinates();

    // create anchors

    this.pushAnchor(ANCHOR_TYPE.first, this.getLine(0).x1, this.getLine(0).y1);

    for (var i = 1; i < linesCount; i++) {
      var line1 = this.getLine(i - 1),
        line2 = this.getLine(i);

      //this.pushAnchor(ANCHOR_TYPE.middle, line1.x1 + line1.x2-line1.x1, line1.y1 + line1.y2-line1.y1);
      this.pushAnchor(ANCHOR_TYPE.main, line1.x2, line1.y2);
      //this.pushAnchor(ANCHOR_TYPE.middle,  line2.x1 + line2.x2-line2.x1, line2.y1 + line2.y2-line2.y1);
    }

    this.pushAnchor(ANCHOR_TYPE.last, this.getLine(linesCount - 1).x2, this.getLine(linesCount - 1).y2);

    this.rebuildPath();
  },

  normalizeCoordinates: function () {
    for (var i = 0; i < this.points.length; i++) {
      this.points[i].x = parseFloat(this.points[i].x);
      this.points[i].y = parseFloat(this.points[i].y);
    }
  },

  getLinesCount: function () {
    return this.points.length - 1;
  },
  _getLine: function (i) {
    return {x1: this.points[i].x, y1: this.points[i].y, x2: this.points[i + 1].x, y2: this.points[i + 1].y};
  },
  getLine: function (i) {
    var line = this._getLine(i);
    line.angle = this.getLineAngle(i);
    return line;
  },
  getLineAngle: function (i) {
    var line = this._getLine(i);
    return Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
  },
  getLineLengthX: function (i) {
    var line = this.getLine(i);
    return (line.x2 - line.x1);
  },
  getLineLengthY: function (i) {
    var line = this.getLine(i);
    return (line.y2 - line.y1);
  },
  getLineLength: function (i) {
    var line = this.getLine(i);
    return Math.sqrt(Math.pow(this.getLineLengthX(i), 2) + Math.pow(this.getLineLengthY(i), 2));
  },

  getAnchors: function () {
    // ������� ��������������� ������
    // ????
    return this.anchors;
  },
  getAnchorsCount: function (type) {
    if (!type)
      return this.anchors.length;
    else {
      var count = 0;
      for (var i = 0; i < this.getAnchorsCount(); i++) {
        var anchor = this.anchors[i];
        if (anchor.getType() == type) {
          count++;
        }
      }
      return count;
    }
  },

  pushAnchor: function (type, x, y, index) {
    if (type == ANCHOR_TYPE.first) {
      index = 0;
      typeIndex = 0;
    } else if (type == ANCHOR_TYPE.last) {
      index = this.getAnchorsCount();
      typeIndex = 0;
    } else if (!index) {
      index = this.anchors.length;
    } else {
      // ��������� anchors, �������� ������� ��� �������, ������� � index
      //var anchor = this.getAnchor()
      for (var i = 0; i < this.getAnchorsCount(); i++) {
        var anchor = this.anchors[i];
        if (anchor.index > index) {
          anchor.index++;
          anchor.typeIndex++;
        }
      }
    }

    var anchor = new Anchor(this.id, ANCHOR_TYPE.main, x, y, index, typeIndex);

    this.anchors.push(anchor);
  },

  getAnchor: function (position) {
    return this.anchors[position];
  },

  getAnchorByType: function (type, position) {
    if (type == ANCHOR_TYPE.first)
      return this.anchors[0];
    if (type == ANCHOR_TYPE.last)
      return this.anchors[this.getAnchorsCount() - 1];

    for (var i = 0; i < this.getAnchorsCount(); i++) {
      var anchor = this.anchors[i];
      if (anchor.type == type) {
        if (position == anchor.position)
          return anchor;
      }
    }
    return null;
  },

  addNewPoint: function (position, x, y) {
    //
    for (var i = 0; i < this.getLinesCount(); i++) {
      var line = this.getLine(i);
      if (x > line.x1 && x < line.x2 && y > line.y1 && y < line.y2) {
        this.points.splice(i + 1, 0, {x: x, y: y});
        break;
      }
    }

    this.rebuildPath();
  },

  rebuildPath: function () {
    var path = [];

    for (var i = 0; i < this.getAnchorsCount(); i++) {
      var anchor = this.getAnchor(i);

      var pathType = ""
      if (i == 0)
        pathType = "M";
      else
        pathType = "L";

// TODO: save previous points and calculate new path just if points are updated, and then save currents values as previous

      var targetX = anchor.x, targetY = anchor.y;
      if (i > 0 && i < this.getAnchorsCount() - 1) {
        // get new x,y
        var cx = anchor.x, cy = anchor.y;

        // pivot point of prev line
        var AO = this.getLineLength(i - 1);
        if (AO < this.radius) {
          AO = this.radius;
        }

        this.isDefaultConditionAvailable = (this.isDefaultConditionAvailable || (i == 1 && AO > 10));
        //console.log("isDefaultConditionAvailable", this.isDefaultConditionAvailable);

        var ED = this.getLineLengthY(i - 1) * this.radius / AO;
        var OD = this.getLineLengthX(i - 1) * this.radius / AO;
        targetX = anchor.x - OD;
        targetY = anchor.y - ED;

        if (AO < 2 * this.radius && i > 1) {
          targetX = anchor.x - this.getLineLengthX(i - 1) / 2;
          targetY = anchor.y - this.getLineLengthY(i - 1) / 2;
          ;
        }

        // pivot point of next line
        var AO = this.getLineLength(i);
        if (AO < this.radius) {
          AO = this.radius;
        }
        var ED = this.getLineLengthY(i) * this.radius / AO;
        var OD = this.getLineLengthX(i) * this.radius / AO;
        var nextSrcX = anchor.x + OD;
        var nextSrcY = anchor.y + ED;

        if (AO < 2 * this.radius && i < this.getAnchorsCount() - 2) {
          nextSrcX = anchor.x + this.getLineLengthX(i) / 2;
          nextSrcY = anchor.y + this.getLineLengthY(i) / 2;
          ;
        }


        var dx0 = (cx - targetX) / 3,
          dy0 = (cy - targetY) / 3,
          ax = cx - dx0,
          ay = cy - dy0,

          dx1 = (cx - nextSrcX) / 3,
          dy1 = (cy - nextSrcY) / 3,
          bx = cx - dx1,
          by = cy - dy1,

          zx = nextSrcX, zy = nextSrcY;

        if (this.showDetails) {
          var c = ProcessDiagramCanvas.g.path("M" + targetX + "," + targetY + "L" + ax + "," + ay).attr({
            stroke: Color.get(255, 153, 51),
            "stroke-dasharray": "- "
          });
          var c = ProcessDiagramCanvas.g.path("M" + nextSrcX + "," + nextSrcY + "L" + bx + "," + by).attr({
            stroke: Color.get(255, 153, 51),
            "stroke-dasharray": "- "
          });
          var c = ProcessDiagramCanvas.g.ellipse(ax, ay, 2, 2).attr({stroke: Color.SlateGrey});
          var c = ProcessDiagramCanvas.g.ellipse(bx, by, 2, 2).attr({stroke: Color.SlateGrey});
          var c = ProcessDiagramCanvas.g.ellipse(cx, cy, this.radius, this.radius).attr({stroke: Color.Gainsboro});
          var c = ProcessDiagramCanvas.g.ellipse(targetX, targetY, 2, 2).attr({fill: Color.red});
          var c = ProcessDiagramCanvas.g.ellipse(nextSrcX, nextSrcY, 2, 2).attr({fill: Color.red});
        }
      } else if (i == 1 && this.getAnchorsCount() == 2) {
        var AO = this.getLineLength(i - 1);
        if (AO < this.radius) {
          AO = this.radius;
        }
        this.isDefaultConditionAvailable = (this.isDefaultConditionAvailable || (i == 1 && AO > 10));
        //console.log("-- isDefaultConditionAvailable", this.isDefaultConditionAvailable);
      }

      // anti smoothing
      if (this.strokeWidth % 2 == 1) {
        targetX += 0.5;
        targetY += 0.5;
      }

      path.push([pathType, targetX, targetY]);

      if (i > 0 && i < this.getAnchorsCount() - 1) {
        path.push(["C", ax, ay, bx, by, zx, zy]);
      }
    }

    if (this.closePath) {
      console.log("closePath:", this.closePath);
      path.push(["Z"]);
    }

    this.path = path;
  },

  transform: function (transformation) {
    this.element.transform(transformation);
  },
  attr: function (attrs) {
    //console.log("attrs: " +attrs, "", this.element);
    // TODO: foreach and set each
    this.element.attr(attrs);
  }
};

function Polygone(points, strokeWidth) {
  /* Array on coordinates:
   * points: [{x: 410, y: 110}, 1
   *			{x: 570, y: 110}, 1 2
   *			{x: 620, y: 240},   2 3
   *			{x: 750, y: 270},     3 4
   *			{x: 650, y: 370}];      4
   */
  this.points = points;

  /*
   * path for graph
   * [["M", x1, y1], ["L", x2, y2], ["C", ax, ay, bx, by, x3, y3], ["L", x3, y3]]
   */
  this.path = [];

  this.anchors = [];

  if (strokeWidth) this.strokeWidth = strokeWidth;

  this.closePath = true;
  this.init();
};


/*
 * Poligone is inherited from Poliline: draws closedPath of polyline
 */

var Foo = function () {
};
Foo.prototype = Polyline.prototype;

Polygone.prototype = new Foo();

Polygone.prototype.rebuildPath = function () {
  var path = [];
  //console.log("Polygone rebuildPath");
  for (var i = 0; i < this.getAnchorsCount(); i++) {
    var anchor = this.getAnchor(i);

    var pathType = ""
    if (i == 0)
      pathType = "M";
    else
      pathType = "L";

    var targetX = anchor.x, targetY = anchor.y;

    // anti smoothing
    if (this.strokeWidth % 2 == 1) {
      targetX += 0.5;
      targetY += 0.5;
    }

    path.push([pathType, targetX, targetY]);
  }
  if (this.closePath)
    path.push(["Z"]);

  this.path = path;
};
/*
Polygone.prototype.transform = function(transformation){
	this.element.transform(transformation);
};
*/
